/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		sidtext.cpp
 * PURPOSE:		SID and GUID conversions to and from text strings
 *				
 */

auto_take<SID> AddRidToSid(SID *Src, ULONG Rid)
{
	ULONG SrcLength = GetSidLength(Src);

	auto_szpod<SID> Ret = auto_szpod_empty(SrcLength + sizeof(ULONG));

	RtlCopyMemory(Ret, Src, SrcLength);

	Ret->SubAuthority[Ret->SubAuthorityCount] = Rid;
	Ret->SubAuthorityCount++;

	return Ret.release();
}

auto_take<WCHAR> Win32SidToText(SID const *sid);
auto_take<SID> Win32TextToSid(PCWSTR src);

auto_take<WCHAR> InternalSidToText(SID const *sid)
{
	DWORD buffchars = MAX_STRING_CHARS;
	auto_szpod<WCHAR> buff(auto_szpod_empty_ct(buffchars)); //ugly, but not expected to overflow
	PWSTR buffct = buff;
	int result;

	result = RaiseOnStdCError(swprintf_s(buffct, buffchars, L"S-1-%hu", sid->IdentifierAuthority.Value[5], -1));

	buffct += result;
	buffchars -= result;

	for(UCHAR i = 0; i < sid->SubAuthorityCount; i++)
	{
		result = RaiseOnStdCError(swprintf_s(buffct, buffchars, L"-%lu", sid->SubAuthority[i]), -1);
	
		buffct += result;
		buffchars -= result;
	}

	return buff.release();
}

auto_take<SID> InternalTextToSid(PCWSTR src) //TODO:needs testing
{
	auto_szpod<SID> sid(auto_szpod_empty(MAX_SID_BYTES));
	USHORT idauth;
	ULONG *subauths = sid->SubAuthority;
	int result;

	//static_assert(SID_MAX_SUB_AUTHORITIES == 15);
		
	result = swscanf_s(src, L"S-1-%hu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu",
		&idauth,
		&subauths[0], &subauths[1], &subauths[2], &subauths[3], &subauths[4], &subauths[5], &subauths[6], &subauths[7], &subauths[8], &subauths[9],
		&subauths[10], &subauths[11], &subauths[12], &subauths[13], &subauths[14]);

	RaiseOnStdCError(result, -1);

	sid->Revision = 1;
	sid->IdentifierAuthority.Value[5] = narrowing_cast<UCHAR>(idauth);

	if(result == 0)
		RaiseWin32Error(ERROR_INVALID_SID);

	sid->SubAuthorityCount = narrowing_cast<UCHAR>(result - 1); //at most 15 could have been retrieved

	return sid.release();
}

typedef BOOL(WINAPI *WIN32_SID_TO_STRING_PROC)(SID const *Sid, LPWSTR* StringSid);
typedef BOOL(WINAPI *WIN32_STRING_TO_SID_PROC)(LPCWSTR StringSid, SID **Sid);

WIN32_SID_TO_STRING_PROC Win32SidToTextImport;
WIN32_STRING_TO_SID_PROC Win32TextToSidImport;

auto_take<WCHAR> Win32SidToText(SID const *sid)
{
	auto_szpod<WCHAR, Win32_LocalHeap_pod_allocator> local_alloc;

	RaiseOnWin32ErrorT(Win32SidToTextImport(sid, local_alloc.assign_indirect()));

	auto_szpod<WCHAR> ret(local_alloc);

	return ret.release();
}

auto_take<SID> Win32TextToSid(PCWSTR src)
{
	auto_szpod<SID, Win32_LocalHeap_pod_allocator> local_alloc;

	RaiseOnWin32ErrorT(Win32TextToSidImport(src, local_alloc.assign_indirect()));

	auto_szpod<SID> ret(local_alloc);

	return ret.release();
}

auto_take<WCHAR> (*SidToText)(SID const *src);
auto_take<SID> (*TextToSid)(PCWSTR src);

void InitSidText()
{
	HMODULE HADVAPI = GetModuleHandle(L"advapi32.dll");

	Win32SidToTextImport = (WIN32_SID_TO_STRING_PROC)GetProcAddress(HADVAPI, "ConvertSidToStringSidW");
	Win32TextToSidImport = (WIN32_STRING_TO_SID_PROC)GetProcAddress(HADVAPI, "ConvertStringSidToSidW");

	SidToText = Win32SidToTextImport ? &Win32SidToText : &InternalSidToText;
	TextToSid = Win32TextToSidImport ? &Win32TextToSid : &InternalTextToSid;
}

auto_take<GUID> TextToGuid(PCWSTR Text)
{
	if(Text)
	{
		auto_ptr<GUID> Guid;
		Guid.realloc_ct(1);
		USHORT DataSh[8]; //intermediate shorts for Guid->Data4; needed because scanf can't scan less than a short at a time

		int ScanCt = swscanf_s(Text, L"%lX-%hX-%hX-%2hX%2hX-%2hX%2hX%2hX%2hX%2hX%2hX", &Guid->Data1, &Guid->Data2, &Guid->Data3, &DataSh[0], &DataSh[1], &DataSh[2], &DataSh[3], &DataSh[4], &DataSh[5], &DataSh[6], &DataSh[7]);

		if(ScanCt != 11)
			return auto_take<GUID>(NULL); //not a valid text GUID

		for(int i = 0; i < 8; i++)
			Guid->Data4[i] = c_cast<UCHAR>(DataSh[i]);

		return Guid.release();
	}
	else
		return auto_take<GUID>(NULL);
}

auto_take<WCHAR> GuidToText(GUID const *Guid)
{	
	if(Guid)
	{
		auto_szpod<WCHAR> Text;
		Text.realloc_ct(38); //38 will always be the size of a text GUID

		RaiseOnStdCError(swprintf_s(Text, 38, L"%08lX-%04hX-%04hX-%02hX%02hX-%02hX%02hX%02hX%02hX%02hX%02hX", Guid->Data1, Guid->Data2, Guid->Data3, Guid->Data4[0], Guid->Data4[1], Guid->Data4[2], Guid->Data4[3], Guid->Data4[4], Guid->Data4[5], Guid->Data4[6], Guid->Data4[7]), -1);

		return Text.release();
	}
	else
		return auto_take<WCHAR>(NULL);
}